﻿// ------------------------------------------------------------------------
// 项目名称：Canroc.Net 
// 版权归属：Canroc（https://gitee.com/canroc）
//
// 许可证信息
// Canroc.Net项目主要遵循 Apache 许可证（版本 2.0）进行分发和使用。许可证位于源代码树根目录中的 LICENSE-APACHE 文件。
//
// 使用条款：
// 1.使用本项目应遵守相关法律法规和许可证的要求。
// 2.不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动。
// 3.任何基于本项目二次开发而产生的一切法律纠纷和责任，我们不承担任何责任
//
// 免责声明
// 对于因使用本代码而产生的任何直接、间接、偶然、特殊或后果性损害，我们不承担任何责任。
//
// 其他重要信息
// Canroc.Net 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。
// ------------------------------------------------------------------------

namespace Canroc.Net.TaskScheduler;

/// <summary>
///     任务持久化(数据库)
/// </summary>
/// <remarks>
///     初始化
/// </remarks>
public class TaskPersistenceToDatabase(IServiceScopeFactory serviceScopeFactory) : IJobPersistence
{
    /// <summary>
    ///     作业调度服务启动时
    /// </summary>
    /// <returns></returns>
    public async Task<IEnumerable<SchedulerBuilder>> PreloadAsync(CancellationToken stoppingToken)
    {
        using var scope = serviceScopeFactory.CreateScope();
        var sysJobRep = scope.ServiceProvider.GetRequiredService<SqlSugarRepository<SysJob>>();
        var sysJobTriggerRep = scope.ServiceProvider.GetRequiredService<SqlSugarRepository<SysJobTrigger>>();

        // 获取所有定义的作业
        var allJobs = App.EffectiveTypes.ScanToBuilders().ToList();
        // 若数据库不存在任何作业，则直接返回
        if (!await sysJobRep.IsAnyAsync(u => true, stoppingToken))
        {
            return await Task.FromResult(allJobs);
        }

        // 遍历所有定义的作业
        foreach (var schedulerBuilder in allJobs)
        {
            // 获取作业信息构建器
            var jobBuilder = schedulerBuilder.GetJobBuilder();

            // 加载数据库数据
            var sysJob = await sysJobRep.GetFirstAsync(u => u.JobId == jobBuilder.JobId, stoppingToken);
            if (sysJob is null)
            {
                continue;
            }

            // 同步数据库数据
            jobBuilder.LoadFrom(sysJob);

            // 获取作业的所有数据库的触发器
            var sysIobTrigger =
                await sysJobTriggerRep.GetListAsync(u => u.JobId == jobBuilder.JobId, stoppingToken);
            // 遍历所有作业触发器
            foreach (var (_, triggerBuilder) in schedulerBuilder.GetEnumerable())
            {
                // 加载数据库数据
                var dbTrigger = sysIobTrigger.FirstOrDefault(u =>
                    u.JobId == jobBuilder.JobId && u.TriggerId == triggerBuilder.TriggerId);
                if (dbTrigger is null)
                {
                    continue;
                }

                triggerBuilder.LoadFrom(dbTrigger).Updated(); // 标记更新
            }

            // 遍历所有非编译时定义的触发器加入到作业中
            foreach (var triggerBuilder in from dbTrigger in sysIobTrigger
                     where schedulerBuilder.GetTriggerBuilder(dbTrigger.TriggerId)?.JobId != jobBuilder.JobId
                     select TriggerBuilder.Create(dbTrigger.TriggerId).LoadFrom(dbTrigger))
            {
                schedulerBuilder.AddTriggerBuilder(triggerBuilder); // 先添加
                triggerBuilder.Updated(); // 再标记更新
            }

            // 标记更新
            schedulerBuilder.Updated();
        }

        return allJobs;
    }

    /// <summary>
    ///     作业计划初始化通知
    /// </summary>
    /// <returns></returns>
    public async Task<SchedulerBuilder> OnLoadingAsync(SchedulerBuilder builder, CancellationToken stoppingToken)
    {
        return await Task.FromResult(builder);
    }

    /// <summary>
    ///     作业计划的 JobDetail 变化时
    /// </summary>
    public async Task OnChangedAsync(PersistenceContext context)
    {
        using var scope = serviceScopeFactory.CreateScope();
        var sysJobRep = scope.ServiceProvider.GetRequiredService<SqlSugarRepository<SysJob>>();

        var sysJob = context.JobDetail.Adapt<SysJob>();
        _ = context.Behavior switch
        {
            PersistenceBehavior.Appended => await sysJobRep.AsInsertable(sysJob).ExecuteCommandAsync(),
            PersistenceBehavior.Updated => await sysJobRep.AsUpdateable(sysJob).Where(u => u.JobId == sysJob.JobId)
                .ExecuteCommandAsync(),
            PersistenceBehavior.Removed => await sysJobRep.AsDeleteable().Where(u => u.JobId == sysJob.JobId)
                .ExecuteCommandAsync(),
            _ => throw Oops.Oh(ErrorCodeEnum.UnKnownOperation)
        };
    }

    /// <summary>
    ///     作业计划的 Trigger 变化时
    /// </summary>
    public async Task OnTriggerChangedAsync(PersistenceTriggerContext context)
    {
        using var scope = serviceScopeFactory.CreateScope();
        var sysJobTriggerRep =
            scope.ServiceProvider.GetRequiredService<SqlSugarRepository<SysJobTrigger>>();

        var jobTrigger = context.Trigger.Adapt<SysJobTrigger>();
        _ = context.Behavior switch
        {
            PersistenceBehavior.Appended => await sysJobTriggerRep.AsInsertable(jobTrigger).ExecuteCommandAsync(),
            PersistenceBehavior.Updated => await sysJobTriggerRep.AsUpdateable(jobTrigger)
                .Where(u => u.TriggerId == jobTrigger.TriggerId && u.JobId == jobTrigger.JobId)
                .ExecuteCommandAsync(),
            PersistenceBehavior.Removed => await sysJobTriggerRep.AsDeleteable()
                .Where(u => u.TriggerId == jobTrigger.TriggerId && u.JobId == jobTrigger.JobId)
                .ExecuteCommandAsync(),
            _ => throw Oops.Oh(ErrorCodeEnum.UnKnownOperation)
        };
    }

    /// <summary>
    ///     作业触发器运行记录
    /// </summary>
    public async Task OnExecutionRecordAsync(PersistenceExecutionRecordContext context)
    {
        using var scope = serviceScopeFactory.CreateScope();
        var logByJobRep =
            scope.ServiceProvider.GetRequiredService<SqlSugarRepository<LogByJob>>();

        var jobTriggerRecord = context.Timeline.Adapt<LogByJob>();
        if (context.Timeline.Result is null || string.IsNullOrEmpty(context.Timeline.Result))
        {
            return;
        }

        _ = await logByJobRep.CopyNew().AsInsertable(jobTriggerRecord).ExecuteCommandAsync();
    }
}